home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / login / sac-1.000 / sac-1 / sac-1.3.1 / sac.c < prev    next >
C/C++ Source or Header  |  1996-05-27  |  28KB  |  1,129 lines

  1. /* $Copyright: $
  2.  * Copyright (c) 1995, 1996 by Steve Baker (ice@mama.indstate.edu)
  3.  * All Rights reserved
  4.  *
  5.  * This software is provided as is without any express or implied
  6.  * warranties, including, without limitation, the implied warranties
  7.  * of merchant-ability and fitness for a particular purpose.
  8.  */
  9. #include <utmp.h>
  10. #include <sys/types.h>
  11. #include <sys/time.h>
  12. #include <time.h>
  13. #include <stdio.h>
  14. #include <unistd.h>
  15. #include <fcntl.h>
  16. #include <paths.h>
  17. #include <string.h>
  18.  
  19. static char *version = "$Version: $ sac v1.3.1 (c) 1995, 1996 by Steve Baker $";
  20.  
  21. /* FSSTND compliance courtesy of Edward S. Marshall */
  22. #ifndef _PATH_WTMP
  23. #define _PATH_WTMP "/var/log/wtmp" /* FSSTND compliant */
  24. #endif
  25.  
  26. #define min(a,b)    ((a) < (b) ? (a) : (b))
  27. #define max(a,b)    ((a) > (b) ? (a) : (b))
  28.  
  29. enum {
  30.   TOTAL      = 0x0001,    /* Total login time */
  31.   DAY     = 0x0002,    /* Daily average */
  32.   USER    = 0x0003,    /* Per user login time */
  33.   AVERAGE = 0x0010,    /* Average login time / login */
  34.   HOUR    = 0x0020,    /* Hourly profile */
  35.   CLIP      = 0x0040,    /* Multiple logins during same time count only once */
  36.   FTP      = 0x0080,    /* Perform ftp accounting only. */
  37.   BOTH      = 0x0100,    /* Perform both normal and ftp accounting. */
  38.   MINMAX  = 0x0200    /* Show min max # logins at one time */
  39. };
  40. enum { FALSE=0, TRUE=1 };
  41. enum { USERLIST, EXCLUDELIST, TTYLIST, HOSTLIST };
  42.  
  43. struct day *mkday();
  44. struct usr *adduser(), *finduser();
  45. void *malloc(), *realloc(), *malloc();
  46. time_t getdate(), churn_time();
  47.  
  48. struct day {
  49.   time_t start, stop;
  50.   short day, month, year, minlogins, maxlogins;
  51.   time_t time;
  52.   time_t h[24];
  53.   u_long logins;
  54.   struct usr *us;
  55.   struct day *nxt;
  56. } *days = NULL, *end=NULL;
  57. /*
  58.  * Keep a pointer at the end of the days list, since we'll usually be
  59.  * adding crap to it, not to days before.  What 'o what do we do about
  60.  * time changes aka time warps?
  61.  */
  62.  
  63. struct usr {
  64.   char user[UT_NAMESIZE+1];
  65.   time_t time;
  66.   time_t h[24];
  67.   u_long logins, xlogins;
  68.   struct usr *nxt;
  69. } *us = NULL;
  70.  
  71. struct exc {
  72.   char user[UT_NAMESIZE+1];
  73.   struct exc *nxt;
  74. } *ex = NULL;
  75.  
  76. struct ttys {
  77.   char line[UT_LINESIZE+1];
  78.   struct ttys *nxt;
  79. } *tty = NULL;
  80.  
  81. struct hosts {
  82.   char host[UT_HOSTSIZE+1];
  83.   char len,ss;        /* Is host a substring? */
  84.   struct hosts *nxt;
  85. } *hosts = NULL;
  86.  
  87. struct user {
  88.   char user[UT_NAMESIZE+1];
  89.   char line[UT_LINESIZE+1];
  90.   char host[UT_HOSTSIZE+1];
  91.   time_t in;
  92.   struct user *nxt;
  93. } *usr = NULL;
  94.  
  95. struct tacacs_utmp {
  96.   char ut_line[8];
  97.   char ut_user[8];
  98.   char ut_host[16];
  99.   time_t ut_time;
  100. };
  101.  
  102. char fix = FALSE, exclude = FALSE, fixtty = FALSE, fixhost = FALSE;
  103. char *back = NULL;
  104. u_short type = TOTAL;
  105. int fd;
  106. time_t total = 0, sd = 0, ed = 0, backtime = 0, curtime = 0;
  107. int ndays = 0, logins = 0, loggedin = 0, minlogins = -1, maxlogins = 0;
  108. signed int sm = 0, em = 0;
  109.  
  110. /*
  111.  * sac [-w wtmp|-] [-dahpcotfFm] [-b hour[:min[:sec]]] [-s start] [-e end]
  112.  *     [[-u] users] [-x [users]] [-T [ttylist]] [-H [hostlist]]
  113.  */
  114. main(argc,argv)
  115. int argc;
  116. char **argv;
  117. {
  118.   char *file = _PATH_WTMP, *start = NULL, *end = NULL, old = FALSE;
  119.   char tacacs = FALSE, ftp = FALSE, both = FALSE;
  120.   char listtype = USERLIST;
  121.   int i, j, n;
  122.   time_t t, s, e;
  123.  
  124.   for(n=i=1;i<argc;i=n) {
  125.     n++;
  126.     if (argv[i][0] == '-') {
  127.       for(j=1;argv[i][j];j++) {
  128.     switch (argv[i][j]) {
  129.       case 'w':
  130.         file = argv[n++];
  131.         break;
  132.       case 'p':
  133.         type = USER | (type & 0xFFF0);
  134.         break;
  135.       case 'd':
  136.         type = DAY | (type & 0xFFF0);
  137.         break;
  138.       case 'a':
  139.         type |= AVERAGE;
  140.         break;
  141.       case 'c':
  142.         type |= CLIP;
  143.         break;
  144.       case 'h':
  145.         type |= HOUR;
  146.         break;
  147.       case 'm':
  148.         type |= MINMAX;
  149.         break;
  150.       case 'F':
  151.         type |= FTP;
  152.         ftp = TRUE;
  153.         break;
  154.       case 'f':
  155.         type |= BOTH;
  156.         both = TRUE;
  157.         break;
  158.       case 'o':
  159.         old = TRUE;
  160.         break;
  161.       case 't':
  162.         tacacs = TRUE;
  163.         break;
  164.       case 'b':
  165.         back = argv[n++];
  166.         break;
  167.       case 's':
  168.         start = argv[n++];
  169.         break;
  170.       case 'e':
  171.         end = argv[n++];
  172.         break;
  173.       case 'x':
  174.         listtype = EXCLUDELIST;
  175.         break;
  176.       case 'T':
  177.         listtype = TTYLIST;
  178.         break;
  179.       case 'u':
  180.         listtype = USERLIST;
  181.         break;
  182.       case 'H':
  183.         listtype = HOSTLIST;
  184.         break;
  185.       case '-':
  186.         if (j == 1) {
  187.           if (!strcmp("--help",argv[i])) usage(4);
  188.         }
  189.       default:
  190.         usage(1);
  191.     }
  192.       }
  193.     } else {
  194.       switch(listtype) {
  195.     case USERLIST:
  196.       adduser(argv[i]);
  197.       fix = TRUE;
  198.       break;
  199.     case EXCLUDELIST:
  200.       addexclude(argv[i]);
  201.       exclude = TRUE;
  202.       break;
  203.     case TTYLIST:
  204.       addtty(argv[i]);
  205.       fixtty = TRUE;
  206.       break;
  207.     case HOSTLIST:
  208.       addhost(argv[i]);
  209.           fixhost = TRUE;
  210.       break;
  211.       }
  212.     }
  213.   }
  214.  
  215.   if (start) {
  216.     if (start[0] == '-' || start[0] == '+') {
  217.       for(i=1;start[i];i++) if (!isdigit(start[i])) usage(2);
  218.       sm = atoi(start);
  219.     } else sd = getdate(start);
  220.   }
  221.   if (end) {
  222.     if (end[0] == '-' || end[0] == '+') {
  223.       t = time(0);
  224.       for(i=1;end[i];i++) if (!isdigit(end[i])) usage(2);
  225.       em = atoi(end);
  226.     } else ed = getdate(end);
  227.   }
  228.   if (back) backtime = (curtime=time(0)) - churn_time(back);
  229.  
  230.   if (!strcmp(file,"-")) fd = 0;
  231.   else fd = open(file,O_RDONLY);
  232.  
  233.   if (both) doitboth();
  234.   else if (ftp) doitftp();
  235.   else if (tacacs) doittacacs();
  236.   else if (old) doitold();
  237.   else doit();
  238.  
  239.   cleanup();
  240.   report();
  241.  
  242.   close(fd);
  243. }
  244.  
  245. usage(n)
  246. int n;
  247. {
  248.   switch (n) {
  249.     case 1:
  250.       fprintf(stderr,"usage: sac [-w wtmp] [-dpfFahcotm] [-s start] [-e end] [-b H[:M[:S]]]\n       [[-u] userlist] [-x [userlist]] [-T [ttylist]] [-H [hostlist]]\n");
  251.       break;
  252.     case 2:
  253.       fprintf(stderr,"sac: Invalid date.  Format: +days | -days | mm/dd/yy\n");
  254.       break;
  255.     case 3:
  256.       fprintf(stderr,"sac: Invalid time.  Format: hours[:minutes[:seconds]]\n");
  257.       break;
  258.     case 4:
  259.       fprintf(stderr,"usage: sac [-w wtmp] [-dpfFahcotm] [-s start] [-e end] [-b H[:M[:S]]]\n       [[-u] userlist] [-x [userlist]] [-T [ttylist]] [-H [hostlist]]\n");
  260.       fprintf(stderr,"    -w wtmp     Read alternate wtmp file.\n");
  261.       fprintf(stderr,"    -d          Perform daily accounting.\n");
  262.       fprintf(stderr,"    -a          Average usage / login.\n");
  263.       fprintf(stderr,"    -h          Show hourly profile.\n");
  264.       fprintf(stderr,"    -p          Per user accounting.\n");
  265.       fprintf(stderr,"    -f          Perform accounting for ftp logins too.\n");
  266.       fprintf(stderr,"    -F          Perform accounting for ftp logins only.\n");
  267.       fprintf(stderr,"    -m          Show min/max # of concurrent logins.\n");
  268.       fprintf(stderr,"    -c          Perform login clipping.\n");
  269.       fprintf(stderr,"    -o          Read old decrepit wtmp format.\n");
  270.       fprintf(stderr,"    -t          Read tacacs wtmp format.\n");
  271.       fprintf(stderr,"    -s start    Display accounting info from `start'.\n");
  272.       fprintf(stderr,"    -e end      Display accounting info up to `end'.\n");
  273.       fprintf(stderr,"    -b H:M:S    Show accounting info from the last few hours:minutes:seconds.\n");
  274.       fprintf(stderr,"    -x [users]  Does not perform accounting for [users].\n");
  275.       fprintf(stderr,"    -T [ttys]   Perform accounting on only those ttys listed.\n");
  276.       fprintf(stderr,"    -H [hosts]  Perform accounting only for the hosts listed.\n");
  277.       fprintf(stderr,"    -u [users]  Perform accounting only for the users listed.\n");
  278.       fprintf(stderr,"    userlist    Perform accounting only for the users listed.\n");
  279.       break;
  280.   }
  281.   exit(1);
  282. }
  283.  
  284. /*
  285.  * Turn "mm/dd/yy" into time in seconds.
  286.  */
  287. time_t getdate(s)
  288. char *s;
  289. {
  290.   struct tm tm;
  291.   int y;
  292.   time_t t;
  293.  
  294. /*
  295.  * <puke>
  296.  * Need a real date parser that can handle different formats and separators
  297.  */
  298.   if (!isdigit(s[0]) || !isdigit(s[1]) || isdigit(s[2])) usage(2);
  299.   if (!isdigit(s[3]) || !isdigit(s[4]) || isdigit(s[5])) usage(2);
  300.   if (!isdigit(s[6]) || !isdigit(s[7]) || s[8]) usage(2);
  301.  
  302.   tm.tm_mon = (((s[0]-'0')*10) + (s[1]-'0')) -1;
  303.   tm.tm_mday = (((s[3]-'0')*10) + (s[4]-'0'));
  304.   y = (((s[6]-'0')*10) + (s[7]-'0'));
  305.   tm.tm_year = (y < 70? 100 + y : y);
  306.   tm.tm_isdst = -1;
  307.   tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
  308.  
  309.   t = mktime(&tm);
  310.   if (t == (time_t)(-1)) usage(2);
  311.   return t;
  312. }
  313.  
  314. time_t churn_time(s)
  315. char *s;
  316. {
  317.   time_t t = 0, mult=3600;
  318.   char nbuf[20], p;
  319.  
  320.   for(p=0;*s;s++) {
  321.     if (*s == ':') {
  322.       nbuf[p] = 0;
  323.       t += atoi(nbuf) * mult;
  324.       p = 0;
  325.       if (mult > 1) mult /= 60;
  326.       else usage(3);
  327.     } else if (isdigit(*s)) {
  328.       if (p < 15) nbuf[p++] = *s;
  329.       else usage(3);
  330.     } else usage(3);
  331.   }
  332.   nbuf[p] = 0;
  333.   t += atoi(nbuf) * mult;
  334.  
  335.   return t;
  336. }
  337.  
  338. doit()
  339. {
  340.   struct utmp u;
  341.   void *ut = &u;
  342.   int n, m;
  343.  
  344.   while (1) {
  345.     for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
  346.       if (m < 0) {
  347.     perror("sac");
  348.     return;
  349.       }
  350.       if (!m) return;
  351.     }
  352.     if (u.ut_time == 0) continue;    /* corrupted wtmp entry! */
  353.     checkday(u);  /* Do we have this day allocated? */
  354.     /* Q: Why does the following bother me? */
  355.     /* A: It may not handle all possible cases. Wtmp documentation sucks.
  356.      *    Programs are also pretty free to put whatever the hell they want
  357.      *    in wtmp.
  358.      */
  359.     if (u.ut_line[0]) {
  360.       if (u.ut_user[0]) {
  361.     if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
  362.     else if (u.ut_type == USER_PROCESS && strncmp("ftp",u.ut_line,3)) login(u);
  363.     else if (u.ut_type == DEAD_PROCESS && strncmp("ftp",u.ut_line,3)) logout(u,0);
  364.     else if (u.ut_type == LOGIN_PROCESS) logout(u,0);
  365.       } else {
  366.     if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
  367.     else logout(u,0);
  368.       }
  369.     }
  370.   }
  371. }
  372.  
  373. /*
  374.  * Do it the old way, where u.ut_type is invalid or doesn't get used.
  375.  * This is for non-standard wtmp's kept by programs such as tacacs.
  376.  * Note: Xterm writes a wtmp entry on exit that looks like a login entry
  377.  *     if we ignore the ut_type field (which is "dead process" in this
  378.  *     case).  This will obviously screw things up.
  379.  *
  380.  * This may not be 100%.
  381.  */
  382. doitold()
  383. {
  384.   struct utmp u;
  385.   void *ut = &u;
  386.   int n, m;
  387.  
  388.   while (1) {
  389.     for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
  390.       if (m < 0) {
  391.     perror("sac");
  392.     return;
  393.       }
  394.       if (!m) return;
  395.     }
  396.  
  397.     if (u.ut_time == 0) continue;    /* corrupted wtmp entry! */
  398.     checkday(u);
  399.     if (u.ut_line[0]) {
  400.       if (u.ut_user[0]) {
  401.     if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
  402.     else if (strcmp("LOGIN",u.ut_user) && strcmp("~",u.ut_line) && strncmp("ftp",u.ut_line,3)) login(u);
  403.     else logout(u,0);
  404.       } else {
  405.     if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
  406.     else logout(u,0);
  407.       }
  408.     }
  409.   }
  410. }
  411.  
  412. /*
  413.  * Do it the tacacs way, with tacacs ancient utmp format, which may work for
  414.  * other ancient programs.
  415.  * This too may not be 100%.
  416.  */
  417. doittacacs()
  418. {
  419.   struct tacacs_utmp t;
  420.   struct utmp u;
  421.   void *ut = &u;
  422.   int n, m;
  423.  
  424.   while (1) {
  425.     for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
  426.       if (m < 0) {
  427.     perror("sac");
  428.     return;
  429.       }
  430.       if (!m) return;
  431.     }
  432.  
  433.     if (t.ut_time == 0) continue;    /* corrupted wtmp entry! */
  434.     /* put tacacs_utmp into a normal utmp for the rest of this */
  435.     u.ut_time = t.ut_time;
  436.     strncpy(u.ut_line,t.ut_line,8);
  437.     strncpy(u.ut_user,t.ut_user,8);
  438.     u.ut_line[8] = u.ut_user[8] = 0;
  439.  
  440.     checkday(u);
  441.     if (u.ut_line[0]) {
  442.       if (u.ut_user[0]) {
  443.     if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
  444.     else if (strcmp("LOGIN",u.ut_user) && strcmp("~",u.ut_line) && strncmp("ftp",u.ut_line,3)) login(u);
  445.     else logout(u,0);
  446.       } else {
  447.     if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
  448.     else logout(u,0);
  449.       }
  450.     }
  451.   }
  452. }
  453.  
  454. /*
  455.  * Perform accounting on ftp logins only.  This handles ftp entries generated
  456.  * from wu-ftpd at least.
  457.  */
  458. doitftp()
  459. {
  460.   struct utmp u;
  461.   void *ut = &u;
  462.   int n, m;
  463.  
  464.   while (1) {
  465.     for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
  466.       if (m < 0) {
  467.     perror("sac");
  468.     return;
  469.       }
  470.       if (!m) return;
  471.     }
  472.  
  473.     if (u.ut_time == 0) continue;    /* corrupted wtmp entry! */
  474.     checkday(u);  /* Do we have this day allocated? */
  475.     if (u.ut_line[0]) {
  476.       if (u.ut_user[0]) {
  477.     if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
  478.     else if (!strncmp("ftp",u.ut_line,3)) login(u);
  479.       } else {
  480.     if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
  481.     else if (!strncmp("ftp",u.ut_line,3)) logout(u,0);
  482.       }
  483.     }
  484.   }
  485. }
  486.  
  487. /*
  488.  * Perform accounting on both normal logins and ftp simultaneously.
  489.  */
  490. doitboth()
  491. {
  492.   struct utmp u;
  493.   void *ut = &u;
  494.   int n, m;
  495.  
  496.   while (1) {
  497.     for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
  498.       if (m < 0) {
  499.     perror("sac");
  500.     return;
  501.       }
  502.       if (!m) return;
  503.     }
  504.  
  505.     if (u.ut_time == 0) continue;    /* corrupted wtmp entry! */
  506.     checkday(u);  /* Do we have this day allocated? */
  507.     if (u.ut_line[0]) {
  508.       if (u.ut_user[0]) {
  509.     if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
  510.     else if (u.ut_type == USER_PROCESS || !strncmp("ftp",u.ut_line,3)) login(u);
  511.     else if (u.ut_type == DEAD_PROCESS) logout(u,0);
  512.     else if (u.ut_type == LOGIN_PROCESS) logout(u,0);
  513.       } else {
  514.     if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
  515.     else logout(u,0);
  516.       }
  517.     }
  518.   }
  519. }
  520.  
  521. /*
  522.  * Make sure the day that the wtmp entry corresponds to, exists in our
  523.  * days list.  If day is less than the starting day by more than a day, or
  524.  * greater than the last day by more than a day, allocate all the days in
  525.  * between as well.
  526.  */
  527. checkday(u)
  528. struct utmp u;
  529. {
  530.   struct day *d, *p;
  531.  
  532.   if (days == NULL) end = days = mkday(u.ut_time);
  533.   else {
  534.     if (u.ut_time < days->start) {
  535.       p = d = mkday(u.ut_time);
  536.       while (p->stop+1 < days->start) {
  537.         p->nxt = mkday(p->stop+1);
  538.     p = p->nxt;
  539.       }
  540.       p->nxt = days;
  541.       days = d;
  542.     } else if (u.ut_time > end->stop) {
  543.       p = d = mkday(end->stop+1);
  544.       while (p->stop < u.ut_time) {
  545.         p->nxt = mkday(p->stop+1);
  546.     p = p->nxt;
  547.       }
  548.       end->nxt = d;
  549.       end = p;
  550.     }
  551.   }
  552. }
  553.  
  554. /*
  555.  * Makes a day entry.  We'll assume a day is exactly 24 hours or 86400
  556.  * seconds long.  I don't know if this is the case or not.  I don't think
  557.  * the time algorithm takes into account leap seconds.
  558.  */
  559. struct day *mkday(t)
  560. time_t t;
  561. {
  562.   struct day *d;
  563.   struct tm *tm;
  564.   int i;
  565.  
  566.   d = malloc(sizeof(struct day));
  567.   tm = localtime(&t);
  568.   tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
  569.  
  570.   d->start = mktime(tm);
  571.   d->stop = d->start + 86399;
  572.   d->nxt = NULL;
  573.   d->us = NULL;
  574.   d->day = tm->tm_mday;
  575.   d->month = tm->tm_mon;
  576.   d->year = tm->tm_year;
  577.   d->logins = d->time = d->maxlogins = 0;
  578.   d->minlogins = -1;
  579.   for(i=0;i<24;i++) d->h[i] = 0;
  580.   ndays++;
  581.   return d;
  582. }
  583.  
  584. login(u)
  585. struct utmp u;
  586. {
  587.   struct user *q;
  588.   struct day *d;
  589.   int l;
  590.  
  591.   /*
  592.    * If we still have a login on this line, it's logged out for sure now!
  593.    * Wtmp could be corrupted.
  594.    */
  595.   logout(u,1);
  596.  
  597.   q = malloc(sizeof(struct user));
  598.   strncpy(q->line,u.ut_line,UT_LINESIZE);
  599.   strncpy(q->user,u.ut_user,UT_NAMESIZE);
  600.   strncpy(q->host,u.ut_host,UT_HOSTSIZE);
  601.   q->user[UT_NAMESIZE] = q->line[UT_LINESIZE] = q->host[UT_HOSTSIZE] = 0;
  602.   q->in = u.ut_time;
  603.   q->nxt = usr;
  604.   usr = q;
  605.   logins++;
  606.  
  607.   if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,q->user)) == NULL) && !(exclude && isexcluded(q->user)) && !(fixtty && !istty(q->line)) && !(fixhost && !ishost(q->host))) {
  608.     l = loggedin++;
  609.     maxlogins = max(maxlogins,loggedin);
  610.     if (minlogins > -1) minlogins = min(minlogins,l);
  611.     else minlogins = l;
  612.     
  613.     if (u.ut_time < end->start) {
  614.       for(d=days;d;d=d->nxt) {
  615.         if (u.ut_time >= d->start && u.ut_time <= d->stop) {
  616.       d->maxlogins = max(d->maxlogins,loggedin);
  617.       if (d->minlogins > -1) d->minlogins = min(d->minlogins,l);
  618.       else d->minlogins = l;
  619.       break;
  620.     }
  621.       }
  622.     } else {
  623.       end->maxlogins = max(end->maxlogins,loggedin);
  624.       if (end->minlogins > -1) end->minlogins = min(end->minlogins,l);
  625.       else end->minlogins = l;
  626.     }
  627.   }
  628. }
  629.  
  630. logout(u,f)
  631. struct utmp u;
  632. char f;
  633. {
  634.   struct user *p, *q, ux;
  635.   struct day *d;
  636.  
  637.   for(p=q=usr;p;) {
  638.     if (!strcmp(u.ut_line,p->line)) {
  639.       release(p,u.ut_time);
  640.  
  641.       if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,p->user)) == NULL) && !(exclude && isexcluded(p->user)) && !(fixtty && !istty(p->line)) && !(fixhost && !ishost(p->host))) {
  642.     loggedin = max(0,loggedin-1);
  643.     if (minlogins > -1) minlogins = min(minlogins,loggedin);
  644.     else minlogins = loggedin;
  645.     
  646.     if (u.ut_time < end->start) {
  647.       for(d=days;d;d=d->nxt) {
  648.         if (u.ut_time >= d->start && u.ut_time <= d->stop) {
  649.           if (d->minlogins > -1) d->minlogins = min(d->minlogins,loggedin);
  650.           else d->minlogins = loggedin;
  651.           break;
  652.         }
  653.       }
  654.     } else {
  655.       if (end->minlogins > -1) end->minlogins = min(end->minlogins,loggedin);
  656.       else end->minlogins = loggedin;
  657.     }
  658.       }
  659.       if (p == usr) {
  660.     usr = p->nxt;
  661.     free(p);
  662.     p = q = usr;
  663.       } else {
  664.         q->nxt = p->nxt;
  665.     free(p);
  666.     p = q->nxt;
  667.       }
  668.       continue;
  669.     }
  670.     q = p;
  671.     p = p->nxt;
  672.   }
  673. }
  674.  
  675. /*
  676.  * logout everyone on reboots or crashes.
  677.  */
  678. do_reboot(u)
  679. struct utmp u;
  680. {
  681.   struct user *p, *q;
  682.   struct day *d;
  683.  
  684.   for(p=usr;p;) {
  685.     release(p,u.ut_time);
  686.     q = p;
  687.     p=p->nxt;
  688.     free(q);
  689.   }
  690.   usr = NULL;
  691.  
  692.   if ((type & MINMAX) == MINMAX) {
  693.     loggedin = 0;
  694.     minlogins = 0;
  695.     
  696.     if (u.ut_time < end->start) {
  697.       for(d=days;d;d=d->nxt)
  698.         if (u.ut_time >= d->start && u.ut_time <= d->stop) {
  699.       d->minlogins = 0;
  700.       break;
  701.     }
  702.     } else end->minlogins = 0;
  703.   }
  704. }
  705.  
  706. /*
  707.  * A utmp entry denoted with a line of "|" denotes the "old" time before
  708.  * netdate updated the time. An entry with line of "{" denotes the new time.
  709.  * The difference in time is applied to the login time of every user still
  710.  * logged in.
  711.  */
  712. changetime(u)
  713. struct utmp u;
  714. {
  715.   static time_t old;
  716.   struct user *p;
  717.   signed long dif;
  718.  
  719.   if (!strcmp("|",u.ut_line)) {
  720.     old = u.ut_time;
  721.     return;
  722.   }
  723.   dif = (signed long)(u.ut_time - old);
  724.   for(p=usr;p;p=p->nxt) p->in -= dif;
  725. }
  726.  
  727. /*
  728.  * Apply login time for users who haven't logged out yet (or that the wtmp file
  729.  * in particular indicates haven't logged out by the EOF) to the days that
  730.  * are in days list.  Login time is not applied to days not listed in the
  731.  * wtmp file (therefor all the days between the first and last wtmp entries).
  732.  * The reason for this is that if you're inspecting an old wtmp file, the
  733.  * wtmp may not indicate all logouts for the last day.  It is not possible
  734.  * to know when the wtmp file really truly ends however, so we apply the
  735.  * minimum of the current time or the ending time for the last day.
  736.  */
  737. cleanup()
  738. {
  739.   time_t t = time(0);
  740.  
  741.   /*
  742.    * Oops, if we're clipping, we have to remove the released time from the
  743.    * list.  This is pretty easily done, by moving usr, instead of using a
  744.    * temporary pointer.
  745.    */
  746.   for(;usr;usr=usr->nxt)
  747.     release(usr,t);
  748. }
  749.  
  750. /*
  751.  * Release a login entry, applying the login time to the particular day
  752.  * entries.
  753.  * A user is logged in on a particular day when:
  754.  *   in  >= start  &&  in  <= stop ||
  755.  *   out >= start  &&  out <= stop ||
  756.  *   in  <  start  &&  out >  stop
  757.  */
  758. release(u,t)
  759. struct user *u;
  760. time_t t;
  761. {
  762.   struct day *p;
  763.   struct usr *up;
  764.   struct user *q;
  765.   signed long tx;
  766.   int i;
  767.  
  768.   if ((signed long)((t - u->in) + 1) < 0) return;
  769.  
  770.   /*
  771.    * Clipping assumes that time moves forward. Only in computers is this
  772.    * not necessarily a safe assumption.
  773.    * Clipping rules:
  774.    *   If (login time < all other login times)
  775.    *      reduce logout time (t) to least login time (-1).
  776.    *   else clip entirely.
  777.    */
  778.   if ((type & CLIP) == CLIP) {
  779.     for(q=usr;q;q=q->nxt) {
  780.       if (q != u && !strcmp(u->user,q->user)) {
  781.     /* throw it out if he's already logged in earlier */
  782.     if (q->in <= u->in) return;
  783.     /* reduce the logout time to the lowest login time. */
  784.     if (q->in < t) t = q->in-1;
  785.       }
  786.     }
  787.   }
  788.  
  789.   if (backtime) {
  790.     if (u->in > curtime || t < backtime) return;
  791.     if (u->in < backtime && t > backtime) u->in = backtime;
  792.     if (u->in < curtime && t > curtime) t = curtime;
  793.   }
  794.  
  795. /*
  796.  * If we've fixed the user list, then we only want to apply the login time
  797.  * if that user is in our user list.
  798.  */
  799.   if (fix && (up = finduser(us,u->user)) == NULL) return;
  800.  
  801. /*
  802.  * Check the exclude user list.
  803.  */
  804.   if (exclude && isexcluded(u->user)) return;
  805.  
  806. /*
  807.  * Check the tty list.
  808.  */
  809.   if (fixtty && !istty(u->line)) return;
  810.  
  811. /*
  812.  * Check the host list.
  813.  */
  814.   if (fixhost && !ishost(u->host)) return;
  815.  
  816.   /* if we're logging usage / user then apply login time to user entry. */
  817.   if ((type & 0x000F) == USER && (((up = finduser(us,u->user)) != NULL) || !fix)) {
  818.     if (up == NULL) up = adduser(u->user);
  819.  
  820.     if (u->in < end->start) {
  821.       for(p=days;p;p=p->nxt) {
  822.     if (u->in >= p->start && u->in <= p->stop) user_apply_hours(u,t,p);
  823.     else if (t >= p->start && t <= p->stop) user_apply_hours(u,t,p);
  824.     else if (u->in < p->start && t > p->stop) user_apply_hours(u,t,p);
  825.       }
  826.     } else user_apply_hours(u,t,end);
  827.     return;
  828.   }
  829.  
  830. /*
  831.  * We go through this for both daily and total. The total will be accumulated
  832.  * at the end since we don't know the starting and ending dates until we're
  833.  * done.
  834.  */
  835.   if (u->in < end->start) {
  836.     /* Ugh, it's probably yesterday, but we've got to start all the way at the
  837.      * beginning. I wonder if double-linking the list would speed things up.
  838.      */
  839.     for(p=days;p;p=p->nxt) {
  840.       if (u->in >= p->start && u->in <= p->stop) {
  841.     p->time += (min(p->stop,t) - u->in) + 1;
  842.     p->logins++;
  843.     if ((type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
  844.       } else if (t >= p->start && t <= p->stop) {
  845.     p->time += (t - max(p->start,u->in)) + 1;
  846.     p->logins++;
  847.     if ((type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
  848.       } else if (u->in < p->start && t > p->stop) {
  849.     p->time += 86400;
  850.     p->logins++;
  851.     if ((type & HOUR) == HOUR)
  852.       for(i=0;i<24;i++) p->h[i] += 3600;
  853.       }
  854.     }
  855.   } else {
  856.     end->time += (min(end->stop,t) - max(end->start,u->in)) + 1;
  857.     end->logins++;
  858.     if ((type & HOUR) == HOUR) apply_hours(u->in,t,end->start,end->h);
  859.   }
  860. }
  861.  
  862. apply_hours(in,out,start,h)
  863. time_t in, out, start, h[24];
  864. {
  865.   int i;
  866.   time_t b, e;
  867.  
  868.   b = start;
  869.   e = start + 3599;
  870.   for(i=0;i<24;i++) {
  871.     if (in >= b && in <= e) h[i] += (min(e,out) - in) + 1;
  872.     else if (out >= b && out <= e) h[i] += (out - max(b,in)) + 1;
  873.     else if (in < b && out > e) h[i] += 3600;
  874.  
  875.     b += 3600;
  876.     e += 3600;
  877.   }
  878. }
  879.  
  880. user_apply_hours(u,out,d)
  881. struct user *u;
  882. time_t out;
  883. struct day *d;
  884. {
  885.   int i;
  886.   time_t b, e;
  887.   struct usr *up;
  888.  
  889.   if ((up = finduser(d->us,u->user)) == NULL) {
  890.     up = malloc(sizeof(struct usr));
  891.     bzero(up,sizeof(struct usr));
  892.     strncpy(up->user,u->user,UT_NAMESIZE);
  893.     up->nxt = d->us;
  894.     d->us = up;
  895.   }
  896.  
  897.   up->time += (min(out,d->stop) - max(u->in,d->start)) + 1;
  898.   if (max(u->in,d->start) == u->in) up->logins++;
  899.   else up->xlogins++;
  900.  
  901.   if ((type & HOUR) == HOUR) {
  902.     b = d->start;
  903.     e = d->start + 3599;
  904.     for(i=0;i<24;i++) {
  905.       if (u->in >= b && u->in <= e) up->h[i] += (min(e,out) - u->in) + 1;
  906.       else if (out >= b && out <= e) up->h[i] += (out - max(b,u->in)) + 1;
  907.       else if (u->in < b && out > e) up->h[i] += 3600;
  908.  
  909.       b += 3600;
  910.       e += 3600;
  911.     }
  912.   }
  913. }
  914.  
  915. struct usr *adduser(s)
  916. char *s;
  917. {
  918.   struct usr *u;
  919.  
  920.   u = malloc(sizeof(struct usr));
  921.   bzero(u,sizeof(struct usr));
  922.   strncpy(u->user,s,UT_NAMESIZE);
  923.   u->nxt = us;
  924.   us = u;
  925.   return us;
  926. }
  927.  
  928. struct usr *finduser(up,s)
  929. struct usr *up;
  930. char *s;
  931. {
  932.   struct usr *u;
  933.  
  934.   for(u=up;u;u=u->nxt)
  935.     if (!strcmp(s,u->user)) return u;
  936.   return NULL;
  937. }
  938.  
  939. addexclude(s)
  940. char *s;
  941. {
  942.   struct exc *u;
  943.  
  944.   u = malloc(sizeof(struct exc));
  945.   strncpy(u->user,s,UT_NAMESIZE);
  946.   u->user[UT_NAMESIZE] = 0;
  947.   u->nxt = ex;
  948.   ex = u;
  949.   return;
  950. }
  951.  
  952. int isexcluded(s)
  953. char *s;
  954. {
  955.   struct exc *u;
  956.  
  957.   for(u=ex;u;u=u->nxt)
  958.     if (!strcmp(s,u->user)) return TRUE;
  959.  
  960.   return FALSE;
  961. }
  962.  
  963. addtty(s)
  964. char *s;
  965. {
  966.   struct ttys *t;
  967.  
  968.   t = malloc(sizeof(struct ttys));
  969.   strncpy(t->line,s,UT_LINESIZE);
  970.   t->line[UT_LINESIZE] = 0;
  971.   t->nxt = tty;
  972.   tty = t;
  973.   return;
  974. }
  975.  
  976. int istty(s)
  977. char *s;
  978. {
  979.   struct ttys *t;
  980.  
  981.   for(t=tty;t;t=t->nxt)
  982.     if (!strcmp(s,t->line)) return TRUE;
  983.  
  984.   return FALSE;
  985. }
  986.  
  987. addhost(s)
  988. char *s;
  989. {
  990.   struct hosts *h;
  991.  
  992.   h = malloc(sizeof(struct hosts));
  993.   strncpy(h->host,s,UT_HOSTSIZE);
  994.   h->host[UT_HOSTSIZE] = 0;
  995.   h->len = strlen(h->host);
  996.   if ((index(h->host,'.') == NULL) || (h->host[h->len-1] == '.')) h->ss = TRUE;
  997.   h->nxt = hosts;
  998.   hosts = h;
  999.   return;
  1000. }
  1001.  
  1002. int ishost(s)
  1003. char *s;
  1004. {
  1005.   struct hosts *h;
  1006.  
  1007.   for(h=hosts;h;h=h->nxt) {
  1008.     if (h->ss) if (!strncmp(s,h->host,h->len)) return TRUE;
  1009.     else if (!strcmp(s,h->host)) return TRUE;
  1010.   }
  1011.  
  1012.   return FALSE;
  1013. }
  1014.  
  1015. report()
  1016. {
  1017.   static char *month[] = {
  1018.     "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
  1019.   };
  1020.   struct day *d;
  1021.   struct usr *u, *up;
  1022.   time_t h[24];
  1023.   int i, nl;
  1024.  
  1025.   /*
  1026.    * Narrow down the days list to the range of days specified by the -s and -e
  1027.    * options.  This seems kludged in to me, but I really can't see how else to
  1028.    * do it.
  1029.    */
  1030.   if (sd || ed || sm || em) {
  1031.     if (sm < 0) sd = end->start + (86400*sm);
  1032.     else if (sm > 0) sd = days->start + (86400*sm);
  1033.     else if (!sd) sd = days->start;
  1034.  
  1035.     if (em < 0) ed = end->start + (86400*em);
  1036.     else if (em > 0) ed = days->start + (86400*em);
  1037.     else if (!ed) ed = end->start;
  1038.  
  1039.     if (sd > ed) exit(0);
  1040.     for(d=days;d;d=d->nxt) {
  1041.       if (d->start == sd) days = d;
  1042.       if (d->start == ed) d->nxt = NULL;
  1043.     }
  1044.   }
  1045.  
  1046.   /* Produce the reports... */
  1047.   switch(type & 0x0f) {
  1048.     case TOTAL:
  1049.       for(ndays=total=0,d=days;d;d=d->nxt) {
  1050.     total += d->time;
  1051.     ndays++;
  1052.       }
  1053.       if (backtime) printf("Total: %12.2f over %s hours.\n", (float)total/3600, back);
  1054.       else printf("Total: %12.2f over %d days.\n", (float)total/3600, ndays);
  1055.       if ((type & AVERAGE) == AVERAGE) printf("Average: %10.2f / day, %10.2f / login\n",((float)total/3600) / ndays,((float)total/3600) / max(1,logins));
  1056.       if ((type & MINMAX) == MINMAX) printf("Logins: %11d   Min: %3d   Max: %3d\n",logins, max(0,minlogins), maxlogins);
  1057.       if ((type & HOUR) == HOUR) {
  1058.     for(i=0;i<24;i++) h[i] = 0;
  1059.     for(d=days;d;d=d->nxt)
  1060.       for(i=0;i<24;i++) h[i] += d->h[i];
  1061.     print_hours(h,total);
  1062.       }
  1063.       break;
  1064.     case DAY:
  1065.       for(d=days;d;d=d->nxt) {
  1066.     printf("%s %2d  total %10.2f",month[d->month],d->day,(float)d->time/3600);
  1067.     if ((type & AVERAGE) == AVERAGE) {
  1068.       if (d->logins)
  1069.         printf("  %5d logins, %8.2f hrs/login",d->logins,((float)d->time/3600)/d->logins);
  1070.       else {
  1071.         printf("     no logins");
  1072.         if ((type & MINMAX) == MINMAX) printf("                    ");
  1073.       }
  1074.     }
  1075.         if ((type & MINMAX) == MINMAX) {
  1076.       if (d->minlogins == -1) d->maxlogins = d->minlogins = d->logins;
  1077.       printf("   Min: %-3d Max: %d",d->minlogins, d->maxlogins);
  1078.     }
  1079.     putchar('\n');
  1080.     if ((type & HOUR) == HOUR) print_hours(d->h,d->time);
  1081.       }
  1082.       break;
  1083.     case USER:
  1084.       for(u=us;u;u=u->nxt) {
  1085.     for(d=days;d;d=d->nxt) {
  1086.       if ((up = finduser(d->us,u->user)) != NULL) {
  1087.         u->time += up->time;
  1088.         u->logins += up->logins;
  1089.         if (d == days) u->logins += up->xlogins;
  1090.         if ((type & HOUR) == HOUR) for(i=0;i<24;i++) u->h[i] += up->h[i];
  1091.       }
  1092.     }
  1093.     printf("\t%-8s %10.2f",u->user,(float)u->time/3600);
  1094.     if ((type & AVERAGE) == AVERAGE) {
  1095.       if (u->logins)
  1096.         printf("\t%5d logins, %10.2f hours / login.",u->logins,((float)u->time/3600) / u->logins);
  1097.       else
  1098.         printf("\t   no logins");
  1099.     }
  1100.     putchar('\n');
  1101.     if ((type & HOUR) == HOUR) print_hours(u->h,u->time);
  1102.       }
  1103.       break;
  1104.   }
  1105. }
  1106.  
  1107. print_hours(h,total)
  1108. time_t h[24], total;
  1109. {
  1110.   static char *bar = "########################################################################";
  1111.   int i, bl = strlen(bar);
  1112.   float p[24], scale, maxp;
  1113.  
  1114.   if (!total) {
  1115.     for(i=0;i<24;i++)
  1116.       printf("%02d-: \n",i);
  1117.   } else {
  1118.     for(i=0;i<24;i++) {
  1119.       p[i] = (float)h[i] / (float)total;
  1120.       if (p[i] > maxp) maxp = p[i];
  1121.     }
  1122.     scale = (float)bl / maxp;
  1123.  
  1124.     for(i=0;i<24;i++) {
  1125.       printf("%02d-: %.*s\n",i,(int)(scale*p[i]),bar);
  1126.     }
  1127.   }
  1128. }
  1129.